const { sqlite, database } = require("../JDBC");
const Path = require('path')
const fs = require('fs')
const zlib = require('zlib');
const CursorBufferReader = require("./bufferReader");
const Bitwise = require('../BitwiseBuffer'); //按位运算库
const encoding = global.encoding; //字符编码工具类

/**
 * pvf数据结构
 * 
 * [pvf_header] + [文件索引列表pvf_indexs] + [文件内容]
 * 
 */


class PVF_Header {
    constructor() {
        this.size = 0;        //4字节  uuid字符串长度        
        this.uuid = "";       //36字节 uuid              
        this.version = 0;     //4字节  版本              
        this.aHeaderSize = 0; //4字节  文件路径数据包的大小
        this.headerCrc = 0;   //4字节  解密密钥          
        this.indexSize = 0;   //4字节  文件数量  

        this.headerSize = 0;   // = this.size+20+this.aHeaderSize  实际上是pvfheader和pvfindexs的总数据量
    }
}

class PVF_Index {
    constructor() {
        this.key = 0          //4字节 文件编号
        this.nameSize = 0     //4字节 文件名数据长度(字节) 
        this.name = null;     //nameSize字节 文件名   
        this.size = 0;        //4字节 文件大小
        this.crc = 0          //4字节 校验码  
        this.offset = 0       //4字节 文件偏移量(从0算，56字节开始  + offset)

        this.alignSize = 0;   //= (this.size + 3) & 0xFFFFFFFC  真实文件大小
    }
}

class StringTable {
    constructor() {
        this.start = 0;        //在file字节码中开始位置
        this.end = 0;          //在file字节码中结束位置
        this.index = 0;        //索引值
        this.content = null;   //字符内容
    }
}


//分段读取
//@param filepath
//@param start:int 开始位 按字节数
//@param end:int   结束位 [start,end]闭区间
const readStream = (filepath, start, end) => new Promise((res, rej) => {
    let data = "";

    const readStream = fs.createReadStream(filepath, {
        start,
        end,
    })

    /**
     * encoding支持类型：
     * ascii     --7 位 ASCII
     * utf8
     * base64
     * binary (=latin1 )   一字节编码 8位
     * hex                  16进制 4位
     */
    readStream.setEncoding('hex'); //读取编码为16进制

    readStream.on('data', chunk => {
        data += chunk;
    })
    readStream.on('end', () => {
        const datalen = data.length;
        const buffer = Buffer.from(data, "hex");
        const bufLen = buffer.length;
        res(buffer);
    })
    readStream.on('error', err => {
        rej(err);
    })
})

//crc解密处理
const crcDecord = (buffer, crc) => {
    const buffReader = new CursorBufferReader(buffer);

    for (let i = 0; i < buffer.length; i += 4) {
        let cursor = buffReader.getCursor();

        let anInt = buffReader.readUint32(); //读取无符号
        let value = anInt ^ 2175242257 ^ crc
        let decord = (value >>> 6) | (value << 26)
        buffer.writeInt32LE(decord, cursor); //写入有符号
    }
}

//读取解密后的文件字节码
//根据pvfindex中偏移量截取buff, 并处理crc进行解密
//@param buff:Buffer  整个pvf文件字节码
//@param pvfHeader
//@param pvfIndex
// @return Buffer
const getFileContentWithcrc = (buffer, pvfHeader, pvfIndex) => {
    let start = pvfHeader.headerSize + pvfIndex.offset;
    let end = start + pvfIndex.alignSize;
    let fileBuffer = buffer.slice(start, end);
    crcDecord(fileBuffer, pvfIndex.crc);

    let fileContentBuffer = Buffer.concat([   //截取size长度,剩下的填充0
        fileBuffer.slice(0, pvfIndex.size),
        Buffer.alloc(pvfIndex.alignSize - pvfIndex.size)
    ])

    return fileContentBuffer;
}

//根据标记字段处理下一个字符字段 
//排除对于0A类型的处理
//@param currentBtye:string<hex>
//@param uInt:number
//@param stringtables:Array
//@param nstrings:Map
const getStringByCurrentByte = (currentBtye, uInt, stringtables, nstrings) => {
    /**
     * 缺一个映射规则，currentBtye并不是严格取值这些，需要映射
     */

    switch (currentBtye) {
        case "02":
            return uInt + "\t";

        case "04":
            {
                const floatBuffer = Buffer.allocUnsafe(4);
                floatBuffer.writeUInt32LE(uInt);
                let float = floatBuffer.readFloatLE();
                float = Math.floor(float * 1000000) / 1000000; //保留6位小数
                return float+"\t";
            }
        case "05":
            {
                let content = stringtables[uInt].content;
                return "\r\n" + content + "\r\n";
            }
        case "06":
        case "08":
            {
                let content = stringtables[uInt].content;
                return `{=${content}}\r\n`;
            }

        case "07":
            {
                let content = stringtables[uInt].content;
                return "`" + content + "`\r\n";
            }
    }
}

//对于0A标记字段专门处理
//多传一个bufferReader
//@param currentBtye:string<hex>
//@param uInt:number
//@param bufferReader:CursorBufferReader
//@param stringtables:Array
//@param nstrings:Map
const getStringByCurrentByte0A = (currentBtye, uInt, bufferReader, stringtables, nstrings) => {

}


//文件字节码转字符串 处理
//对一般文件
const getFileContent = (pvfHeader, pvfIndex, bufferReader, stringtables, nstrings) => {
    let content = "#PVF_File\r\n";

    if (bufferReader.remaining() <= 7) {
        return content;
    }

    while (bufferReader.hasRemaining()) {
        if (bufferReader.remaining() >= 5) {
            let currentBtye = bufferReader.readBytes(1).toString("hex")
            let uint = bufferReader.readUint32();

            if (currentBtye == "0A") {
                content += getStringByCurrentByte0A(currentBtye,uint,bufferReader,stringtables, nstrings);

            } else {
                content += getStringByCurrentByte(currentBtye,uint,stringtables, nstrings);
            }
        } else {
            break;
        }
    }

    return content;
}

//lst文件处理
const getLstContent = (pvfHeader, pvfIndex, bufferReader, stringtables) => {
    let map = new Map();

    //读取两字节进行校验
    let vailed = bufferReader.readUint16();
    if (vailed != 53424) {
        return {};
    }

    if (pvfIndex.name.indexOf("operatingvalue.lst") != -1) {
        let a;
    }

    if (pvfIndex.name.indexOf("n_quest/epicquest.lst") != -1) {
        let b;
    }

    while (bufferReader.hasRemaining()) {
        if (bufferReader.remaining() - 10 >= 0) {

            let cByte1 = bufferReader.readBytes(1).toString("hex");  //currentBtye
            let uint1 = bufferReader.readUint32();
            let str1 = getStringByCurrentByte(cByte1, uint1, stringtables).trim();

            let cByte2 = bufferReader.readBytes(1).toString("hex");  //currentBtye
            let uint2 = bufferReader.readUint32();
            let str2 = getStringByCurrentByte(cByte2, uint2, stringtables).replaceAll("`", "").trim();

            map.set(str1, str2);
        } else {
            break;
        }
    }

    return map;
}


//输出文件
//@param content:Buffer|string 文件内容
//@param outPath:string 输出跟目录
//@param relativePath:string 相对根目录路径
const outputFile = (content, outPath, relativePath) => {
    let fileContent;

    if (typeof (content) === "string") {
        fileContent = content;
    } else if (Buffer.isBuffer(content)) {
        fileContent = content;
    } else {
        fileContent = toString(content);//尝试转成string
    }

    let outpath = Path.join(outPath, relativePath);
    let outdirPath = Path.dirname(outpath);

    if (!fs.existsSync(outdirPath)) {
        fs.mkdirSync(outdirPath, { recursive: true });
    }

    fs.writeFile(outpath, fileContent, (err) => {
        if (err) {
            console.error("输出文件错误!", outPath, relativePath, outpath, err);
        }
    })
}


const pvf = {

    //解压pvf文件
    //@param filepath:string  文件路径 @不校验
    //@param outpath:string   输出文件夹路径 需要提前建好 @不校验
    decomprFile: (filepath, outpath) => new Promise((resolve, reject) => {
        console.log("pvf.decomprFile", filepath, outpath);

        const stat = fs.statSync(filepath);
        const pvfHeader = new PVF_Header();
        const pvfIndexs = new Map();     //<string,Pvf_Index>  key为文件路径 value为pvfIndex对象
        const stringtables = [];  //<StringTable>      stringtable.bin文件解析内容
        const nstrings = new Map(); //<string,Map<string,string>>     key为索引字段 value为内容   n_string.lst文件

        //解析文件结构 pvfHeader
        //先读取前100字节
        readStream(filepath, 0, 100).then(buffer => {
            const bufferReader = new CursorBufferReader(buffer);

            pvfHeader.size = bufferReader.readUint32();
            pvfHeader.uuid = bufferReader.readLatin1(pvfHeader.size);
            pvfHeader.version = bufferReader.readUint32();
            pvfHeader.aHeaderSize = bufferReader.readUint32();
            pvfHeader.headerCrc = bufferReader.readUint32();
            pvfHeader.indexSize = bufferReader.readUint32();

            pvfHeader.headerSize = pvfHeader.size + 20 + pvfHeader.aHeaderSize;
            console.log("解析完成pvfHeader", pvfHeader)
            initPvfIndexs();
        })

        //解析文件树 pvfIndexs
        const initPvfIndexs = () => {
            //从pvfHeader.headerSize开始读, 读pvfHeader.aHeaderSize字节
            const start = pvfHeader.size + 20;
            const end = start + pvfHeader.aHeaderSize - 1;

            readStream(filepath, start, end).then(buffer => {
                console.log("读取pvfIndex全部数据包", buffer.length, pvfHeader.aHeaderSize);

                //解密数据包
                crcDecord(buffer, pvfHeader.headerCrc);
                const bufferReader = new CursorBufferReader(buffer);

                for (let i = 0; i < pvfHeader.indexSize; i++) {
                    const pvfIndex = new PVF_Index();

                    pvfIndex.key = bufferReader.readUint32();
                    pvfIndex.nameSize = bufferReader.readUint32();
                    pvfIndex.name = bufferReader.readCP949(pvfIndex.nameSize).replaceAll("\\", "/"); //文件名大小写不处理 保持原样
                    pvfIndex.size = bufferReader.readUint32();
                    pvfIndex.crc = bufferReader.readUint32();
                    pvfIndex.offset = bufferReader.readUint32();
                    pvfIndex.alignSize = (pvfIndex.size + 3) & 0xFFFFFFFC

                    pvfIndexs.set(pvfIndex.name.toLowerCase(), pvfIndex)//索引key处理为小写
                }

                //输出到文件夹
                {
                    const content = Array.from(pvfIndexs.keys()).join("\r\n");
                    outputFile(content, outpath, "!FileTree.txt");
                }

                console.log("解析pvfIndexs完成");
                buildDBCache();
                readContext();
            })
        }

        //读取文件内容
        const readContext = async () => {

            fs.readFile(filepath, async (err, buffer) => {
                if (err) {
                    console.error(err);
                    return;
                }

                const stringtablePvfIndex = pvfIndexs.get('stringtable.bin');
                if (stringtablePvfIndex) {
                    await readStringtable(buffer, stringtablePvfIndex);
                }

                const nstringPvfIndex = pvfIndexs.get('n_string.lst');
                if (nstringPvfIndex) {
                    await readNString(buffer, nstringPvfIndex);
                }

                await readScripts(buffer);
            })

            //读取stringtable.bin
            //结果写入stringtable{}
            const readStringtable = (buffer, pvfIndex) => new Promise((res, rej) => {
                console.log("readStringtable", pvfIndex);
                const scriptBuffer = getFileContentWithcrc(buffer, pvfHeader, pvfIndex);
                const bufferReader = new CursorBufferReader(scriptBuffer);  //34302360字节

                const stringLength = bufferReader.readUint32(); //前4字节 string组数量   //937202

                let start = bufferReader.readUint32();
                for (let i = 0; i < stringLength; i++) {
                    const stringTable = new StringTable();

                    stringTable.index = i;
                    stringTable.start = start;
                    stringTable.end = bufferReader.readUint32();

                    //读取content
                    let cursor = bufferReader.getCursor(); //记录一下游标
                    let err = bufferReader.seek(stringTable.start + 4, 0); //移动到偏移位置

                    if (err == false) {
                        console.log("start", stringTable.start, stringTable.end)
                    }

                    let contentBuffer = bufferReader.readBytes(stringTable.end - stringTable.start);
                    bufferReader.seek(cursor, 0); //回到之前的游标

                    let content = encoding.convert(contentBuffer, 'UTF-8', 'BIG5').toString().trim();
                    stringTable.content = content;

                    stringtables.push(stringTable);
                    start = stringTable.end;
                }


                console.log("读取stringtable.bin完成")

                //输出文件
                {
                    let content = stringtables.map(e => e.content).join("\r\n"); //@TEST 是否超过string上线
                    outputFile(content, outpath, "stringtable.bin");
                }

                res();
            })

            //读取n_string.lst
            //结果写入nstring{}
            const readNString = (buffer, pvfIndex) => new Promise((res, rej) => {
                console.log("readNString", pvfIndex);
                const scriptBuffer = getFileContentWithcrc(buffer, pvfHeader, pvfIndex);
                const bufferReader = new CursorBufferReader(scriptBuffer);  //34302360字节
                const lstMap = getLstContent(pvfHeader, pvfIndex, bufferReader, stringtables); //得到 map<code,filePath>

                let lstContent = "";

                //继续解析map
                lstMap.forEach((filePath, code) => {
                    const filePathLowercase = filePath.toLowerCase(); //先转成小写，索引key都是小写
                    // console.log(code, filePath)

                    lstContent += `${code}   \`${filePath}\`\r\n`;

                    const childPvfIndex = pvfIndexs.get(filePathLowercase);
                    if (!childPvfIndex) {
                        return;
                    }

                    const scriptBuffer = getFileContentWithcrc(buffer, pvfHeader, childPvfIndex);
                    const fileContentBuffer = encoding.convert(scriptBuffer, 'UTF-8', 'BIG5')
                    const fileContent = fileContentBuffer.toString();

                    // console.log("fileContent.length", fileContent.length)
                    // console.log("fileContent", fileContent)

                    //输出文件
                    {
                        outputFile(fileContentBuffer, outpath, filePath);
                    }

                    //处理为key-value形式
                    const fileMap = new Map();
                    const rows = fileContent.split("\r\n");
                    rows.forEach(row => {
                        //注释行跳过
                        if (row.indexOf("//") == 0) {
                            return;
                        }

                        //未知作用 跳过
                        if (row.indexOf(">") == -1) {
                            return
                        }

                        const entity = row.split(">");
                        fileMap.set(entity[0].trim(), entity[1].trim());
                    });

                    nstrings.set(filePath, fileMap);
                })

                //输出n_string.lst
                outputFile(lstContent, outpath, "n_string.lst");

                console.log("读取n_string.lst完成")
                res();
            })

            //读取剩余文件
            //结果直接写入输出路径
            const readScripts = (buffer) => new Promise((res, rej) => {
                for (let pvfIndex of pvfIndexs.values()) {

                    //跳过 stringtable.bin,n_string.lst,*.str
                    if (pvfIndex.name === "stringtable.bin" || pvfIndex.name === "n_string.lst" || pvfIndex.name.indexOf(".str") != -1) {
                        continue;
                    }

                    const scriptBuffer = getFileContentWithcrc(buffer, pvfHeader, pvfIndex);
                    const bufferReader = new CursorBufferReader(scriptBuffer);

                    if (pvfIndex.name.indexOf(".lst") != -1) {
                        let lstMap = getLstContent(pvfHeader, pvfIndex, bufferReader, stringtables);
                        let lstContent = "";
                        lstMap.forEach((filePath, code) => {
                            lstContent += `${code}   \`${filePath}\`\r\n`;
                        });

                        console.log("输出lst文件", pvfIndex.name)
                        outputFile(lstContent, outpath, pvfIndex.name);
                    } else {
                        fileContext = getFileContent(pvfHeader, pvfIndex, bufferReader, stringtables, nstrings);
                        outputFile(fileContext, outpath, pvfIndex.name);
                    }
                }

                //全部处理完毕
                console.log("全部处理完毕");
                resolve();
            })
        }

        //将pvfindexs写入database
        //无需等待
        const buildDBCache = () => {

        };
    }),


    //打包pvf文件夹
    //@param filepath:string  文件夹路径  @不校验
    //@param outpath:string   输出pvf路径 @不校验
    compressDirToPvf: (dirpath, outpath) => new Promise((res, rej) => {

    })
}


module.exports = pvf